package com.ying.user.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.ying.common.entity.Response;
import com.ying.common.entity.ResponseCode;
import com.ying.common.exception.SecurityException;
import com.ying.user.dao.MmRolePermissionDao;
import com.ying.user.dao.MmUserDao;
import com.ying.user.dao.MmUserRoleDao;
import com.ying.user.entity.*;
import com.ying.user.entity.dto.*;
import com.ying.user.service.MmUserService;
import com.ying.user.utils.DateUtils;
import com.ying.user.utils.JwtUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

@Service
public class MmUserServiceImpl implements MmUserService {

    private static final Logger logger = LoggerFactory.getLogger(MmUserServiceImpl.class);
    private static final String KEY = "telephone:";

    @Autowired
    private MmUserDao mmUserDao;
    @Autowired
    private MmUserRoleDao mmUserRoleDao;
    @Autowired
    private MmRolePermissionDao mmRolePermissionDao;
    @Autowired
    private JwtUtils jwtUtils;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Function<MmUserRegister, Response> register() {
        return (mmUserRegister) -> {
            try {
                Response response = new Response();

                // 短信验证码输入验证
                String key = KEY + mmUserRegister.getTelephone();
                String verifyCode = stringRedisTemplate.opsForValue().get(key);
                if (!mmUserRegister.getVerifyCode().equals(verifyCode)) {
                    return new Response().failure("验证码输入有误");
                }
                // 二次密码输入验证
                if (!mmUserRegister.getPassword().equals(mmUserRegister.getRePassword())) {
                    return new Response().failure("两次密码输入不一致");
                }

                // 校验该手机号是否存在
                MmUserQuery mmUserQuery = new MmUserQuery();
                mmUserQuery.createCriteria().andTelephoneEqualTo(mmUserRegister.getTelephone());
                List<MmUser> mmUsers = mmUserDao.selectByQuery(mmUserQuery);
                if (!CollectionUtils.isEmpty(mmUsers) && mmUsers.size() > 0) {
                    return new Response().failure("该手机号已被注册");
                }
                MmUser mmUser = new MmUser();
                mmUser.setTelephone(mmUserRegister.getTelephone());
                mmUser.setPassword(passwordEncoder.encode(mmUserRegister.getPassword()));
                mmUser.setCreateTime(DateUtils.getDateTime());
                mmUser.setUpdateTime(DateUtils.getDateTime());
                int i = mmUserDao.insertSelective(mmUser);

                return i >0 ? response.success("注册成功") : response.failure("注册失败");
            } catch (Exception e) {
                logger.error("注册失败", e);
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            }
            return new Response().failure("注册失败");
        };
    }

    @Override
    public Function<MmUserLogin, Response> login() {
        return (mmUserLogin) -> {
            Authentication authentication = authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(mmUserLogin.getTelephone(), mmUserLogin.getPassword()));
            SecurityContextHolder.getContext().setAuthentication(authentication);
            String jwt = jwtUtils.createJWT(authentication, mmUserLogin.getRememberMe());
            return new Response().success(jwt);
        };
    }

    @Override
    public Function<HttpServletRequest, Response> logout() {
        return (request) -> {
            try {
                // 设置JWT过期
                jwtUtils.invalidateJWT(request);
            } catch (SecurityException e) {
                throw new SecurityException(ResponseCode.UNAUTHORIZED);
            }
            return new Response().userLoginDefined(ResponseCode.LOGOUT);
        };
    }

    @Override
    @Transactional
    public Function<MmUserAdd, Response> add() {
        return (mmUserAdd) -> {
            int i = 0;
            Response response = new Response();
            Long userId = mmUserAdd.getUserId();
            Long roleId = mmUserAdd.getRoleId();
            Long permissionId = mmUserAdd.getPermissionId();

            MmUserRoleQuery mmUserRoleQuery = new MmUserRoleQuery();
            mmUserRoleQuery.createCriteria().andUserIdEqualTo(userId).andRoleIdEqualTo(roleId);
            List<MmUserRole> mmUserRoles = mmUserRoleDao.selectByQuery(mmUserRoleQuery);
            if (CollectionUtils.isEmpty(mmUserRoles) && mmUserRoles.size() <= 0) {
                MmUserRole mmUserRole = new MmUserRole();
                mmUserRole.setUserId(userId);
                mmUserRole.setRoleId(roleId);
                mmUserRole.setCreateTime(DateUtils.getDateTime());
                mmUserRole.setUpdateTime(DateUtils.getDateTime());
                i += mmUserRoleDao.insertSelective(mmUserRole);
            }

            MmRolePermission mmRolePermission = new MmRolePermission();
            mmRolePermission.setRoleId(roleId);
            mmRolePermission.setPermissionId(permissionId);
            mmRolePermission.setCreateTime(DateUtils.getDateTime());
            mmRolePermission.setUpdateTime(DateUtils.getDateTime());
            i += mmRolePermissionDao.insertSelective(mmRolePermission);

            return i > 0 ? response.success("用户添加角色权限成功") : response.failure("用户添加角色权限失败");
        };
    }

    @Override
    public Function<String, Response> sendSms() {
        return (telephone) -> {
            if (StringUtils.isBlank(telephone)) {
                return new Response().failure("手机号不能为空");
            }
            String key = KEY + telephone;
            //生成6位随机验证码
            String verifyCode = RandomUtil.randomNumbers(6);
            //往 redis 存放验证码，并设置验证码的超时时间，不能超过2分钟
            stringRedisTemplate.opsForValue().set(key, verifyCode, 1, TimeUnit.DAYS);
            //通知 RabbitMQ 发送短信----->消息队列
            Map<String, String> map = new HashMap<>();
            map.put("telephone", telephone);
            map.put("verifyCode", verifyCode);
            //穿件消息队列
            rabbitTemplate.convertAndSend("sms", map);
            return new Response().success("已发送");
        };
    }

    @Override
    public Function<MmUserForget, Response> forget() {
        return (mmUserForget) -> {
            // 短信验证码输入验证
            String key = KEY + mmUserForget.getTelephone();
            String verifyCode = stringRedisTemplate.opsForValue().get(key);
            if (!mmUserForget.getVerifyCode().equals(verifyCode)) {
                return new Response().failure("验证码输入有误");
            }
            return new Response().success("验证成功");
        };
    }

    @Override
    public Function<MmUserReset, Response> reset() {
        return (mmUserReset) -> {
            Response response = new Response();
            try {
                // 二次密码输入验证
                if (!mmUserReset.getPassword().equals(mmUserReset.getRePassword())) {
                    return response.failure("两次密码输入不一致");
                }
                MmUser mmUser = new MmUser();
                MmUserQuery mmUserQuery = new MmUserQuery();
                mmUserQuery.createCriteria().andTelephoneEqualTo(mmUserReset.getTelephone());
                mmUser.setPassword(passwordEncoder.encode(mmUserReset.getPassword()));
                mmUser.setUpdateTime(DateUtils.getDateTime());
                int i = mmUserDao.updateByQuerySelective(mmUser, mmUserQuery);
                return i > 0 ? response.success("重置成功") : response.failure("重置失败");
            } catch (Exception e) {
                logger.error("重置失败", e);
            }
            return response.failure("重置失败");
        };
    }


//    public static void main(String[] args) {
//        Predicate predicate = x -> x.equals(NumberUtils.toByte("1"));
//        MmUser user = new MmUser(NumberUtils.toLong("1"), "颖颖", "77", "xxx", NumberUtils.toByte("0"), DateUtils.getDateTime(), DateUtils.getDateTime());
//        System.out.println("男生：" + predicate.test(user.getGender()));
//
//        Consumer consumer = System.out::println;
//        consumer.accept("高考必胜！");
//
//        Function<MmUser, String> function = MmUser::getUsername;
//        String username = function.apply(user);
//        System.out.println(username);
//
//        Supplier supplier = () -> Integer.valueOf(BigDecimal.TEN.toString());
//        System.out.println(supplier.get());
//
//        UnaryOperator<Boolean> unaryOperator = unary -> !unary;
//        Boolean apply2 = unaryOperator.apply(true);
//        System.out.println(apply2);
//
//        BinaryOperator<Integer> operator = (x, y) -> x * y;
//        Integer integer = operator.apply(2, 3);
//        System.out.println(integer);
//    }

//    public static void main(String[] args) {
//
//        HashMap<Object, Object> map = new HashMap<>();
//        map.put("a", "A");
//        map.put("b", "B");
//        map.put("c", "C");
//        Iterator<Map.Entry<Object, Object>> iterator = map.entrySet().iterator();
//        while (iterator.hasNext()) {
//            // next 会指向下一位，遍历map要key与value不能同时获取
//            Map.Entry<Object, Object> next = iterator.next();
////            System.out.println(iterator.next().getKey() + "----" + iterator.next().getValue());
//            System.out.println(next.getKey() + "----" + next.getValue());
//        }
//    }

}
